package jmine.tec.services.execution;

import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.List;

import jmine.tec.services.api.Service;
import jmine.tec.services.api.ServiceExecutionException;
import jmine.tec.services.api.ServicesPage;
import jmine.tec.services.api.execution.ExecutorParams;
import jmine.tec.services.api.execution.ServicesPageExecutor;
import jmine.tec.services.io.ServiceBean;
import jmine.tec.services.io.ServicesPageBean;
import bancosys.tec.services.input.driver.bean.ServicePageHeader;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.XmlFriendlyReplacer;
import com.thoughtworks.xstream.io.xml.XppDriver;

/**
 * Executor que transforma uma lista de servicesPage em um arquivo XML.
 * 
 * @author Rafael Volpato
 */
public class ExportXMLServicesPageExecutor implements ServicesPageExecutor {

    private Writer writer;

    /**
     * Constrói um executor que utilizará a saída padrão.
     */
    public ExportXMLServicesPageExecutor() {
        this(System.out);
    }

    /**
     * Constrói um executor que utilizará o {@link OutputStream}.
     * 
     * @param stream {@link OutputStream} utilizado na saída do XML.
     */
    public ExportXMLServicesPageExecutor(OutputStream stream) {
        this(new OutputStreamWriter(stream));
    }

    /**
     * Constrói um executor que utilizará o {@link Writer}.
     * 
     * @param writer {@link Writer} utilizado na saída do XML.
     */
    public ExportXMLServicesPageExecutor(Writer writer) {
        this.writer = writer;
    }

    /**
     * Transforma a lista de servicesPage passada como parametro em um arquivo XML.
     * 
     * @param servicesPages lista de paginas de servicos
     */
    public void execute(List<ServicesPage> servicesPages) {
        ServicePageHeader servicePageHeader = new ServicePageHeader();

        for (ServicesPage servicesPage : servicesPages) {
            ServicesPageBean servicePageBean = this.convertServicesPage(servicesPage);
            servicePageHeader.addSheet(servicePageBean);
        }

        // cria nova instancia de XStream.
        XStream stream = new XStream(new XppDriver(new XmlFriendlyReplacer("_-", "_")));

        // configura o XStream para detectar annotations.
        stream.autodetectAnnotations(true);

        stream.toXML(servicePageHeader, this.writer);
    }

    /**
     * Converter uma {@link ServicesPage} em uma {@link ServicesPageBean}.
     * 
     * @param servicesPage servicesPage
     * @return servicesPageBean
     */
    private ServicesPageBean convertServicesPage(ServicesPage servicesPage) {
        ServicesPageBean servicesPageBean = new ServicesPageBean();
        servicesPageBean.setName(servicesPage.getName());

        for (Service<?> service : servicesPage.getServices()) {
            ServiceBean serviceBean = this.convertService(service);
            servicesPageBean.addService(serviceBean);
        }

        return servicesPageBean;
    }

    /**
     * Converte um {@link Service} em um {@link ServiceBean}.
     * 
     * @param service service
     * @return serviceBean
     */
    private ServiceBean convertService(Service<?> service) {
        ServiceBean serviceBean = new ServiceBean();

        serviceBean.setAction(service.getAction());
        serviceBean.setName(service.getServiceName());
        serviceBean.setServiceLocation(service.getServiceLocation());
        serviceBean.setParameters(service.getServiceProperties().getProperties());

        return serviceBean;
    }

    /**
     * {@inheritDoc}
     */
    public void execute(List<ServicesPage> servicesPages, ExecutorParams params) throws ServiceExecutionException {
        this.execute(servicesPages);
    }
}
